﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;

namespace SharpPad.Forms
{
    public partial class ManageAssemblyReferences : ModalDialog
    {
        private Dictionary<String, String> _cache;

        public List<AssemblyReference> References { get; set; }

        public ManageAssemblyReferences() : this(new List<String>(), null)
        { }

        public ManageAssemblyReferences(IEnumerable<String> paths, Dictionary<String,String> assemblyCache)
        {
            Contract.Requires(paths != null && paths.Count() > 0);

            _cache = assemblyCache ?? new Dictionary<string, string>();

            InitializeComponent();

            if (paths == null) return;

            var assemblies = paths.ToArray();
            References = GetAssemblyReferences(assemblies).ToList();
        }

        private void RefreshList()
        {
            if (References.Count == 0) return;
            AssemblyList.SuspendLayout();

            AssemblyList.Items.Clear();

            foreach (var assemblyReference in References)
            {
                var item = AssemblyList.Items.Add(assemblyReference.Name);
                item.Name = "AssemblyName";
                item.UseItemStyleForSubItems = true;
                var token = new ListViewItem.ListViewSubItem(item, assemblyReference.Token) {Name = "Token"};
                item.SubItems.Add(token);
                var version = new ListViewItem.ListViewSubItem(item, assemblyReference.Version) {Name = "Version"};
                item.SubItems.Add(version);
                var path = new ListViewItem.ListViewSubItem(item, assemblyReference.Path) {Name = "Path"};
                item.SubItems.Add(path);
            }

            foreach (ColumnHeader column in AssemblyList.Columns)
            {
                column.Width = -2; // Autowidth.
            }

            AssemblyList.ResumeLayout(true);

        }

        private void ManageAssemblyReferencesLoad(object sender, EventArgs e)
        {
            // Populate our list view.
            RefreshList();
        }

        private void AddReferenceClick(object sender, EventArgs e)
        {
            // Save the current directory for manual restore later.
            var currentDirectory = Directory.GetCurrentDirectory();

            // Show file picker.
            var result = AddAssemblyDialog.ShowDialog();
            
            if (result != DialogResult.OK) return;

            var files = AddAssemblyDialog.FileNames;

            // Process our references
            var refs = GetAssemblyReferences(files);
            References = References.Union(refs).ToList();

            // Now we can restore.
            Directory.SetCurrentDirectory(currentDirectory);

            RefreshList();
        }

        private void RemoveClick(object sender, EventArgs e)
        {
            if (AssemblyList.SelectedItems.Count == 0) return;
            foreach (ListViewItem item in AssemblyList.SelectedItems)
            {
                var pathName = item.SubItems["Path"].Text;
                var reference = References.SingleOrDefault(x => x.Path == pathName);
                References.Remove(reference);
                item.Remove();
            }
        }

        public IEnumerable<AssemblyReference> GetAssemblyReferences(string[] filePaths)
        {
            var references = new List<AssemblyReference>();
            var currentAssemblyPath = Assembly.GetExecutingAssembly().Location;

            foreach (var file in filePaths)
            {
                AssemblyReference assemblyRef = null;
                Assembly assembly = null;
                var fileInfo = new FileInfo(file);

                if (String.Compare(fileInfo.FullName, currentAssemblyPath) == 0) continue;
                
                if (fileInfo.Exists)
                {
                    assembly =  Assembly.LoadFile(fileInfo.FullName);
                    assemblyRef = new AssemblyReference(assembly) {Path = file};
                }
                else
                {
                    // Try to find it in the cache first.
                    String value;
                    _cache.TryGetValue(file, out value);

                    if (!String.IsNullOrEmpty(value)) fileInfo = new FileInfo(value);
                    if (fileInfo.Exists) assembly = Assembly.LoadFile(fileInfo.FullName);
                    if (assembly != null) assemblyRef = new AssemblyReference(assembly);

                    if (assembly == null)
                    {
                        // If that doesn't work, try to load from the GAC.
                        try
                        {
                            assembly = Assembly.Load(file);
                            var path = assembly.Location;
                            assemblyRef = new AssemblyReference(assembly) { Path = path };
                        }
                        catch (Exception ex)
                        {
                            Trace.TraceError(ex.Message);
                        }
                    }
                }
                if (assemblyRef != null && !references.Contains(assemblyRef))
                    references.Add(assemblyRef);
            }
            return references.AsEnumerable();
        }

    }
}
